/******************************************************************************

    Copyright (C) 2006 Alessio Stalla (alessiostalla@gmail.com)

    This file is part of PrettyProlog.

    PrettyProlog is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    PrettyProlog is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with PrettyProlog; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    See the file COPYING for further details.

 *****************************************************************************/

package prettyprolog.gui;

import java.awt.BorderLayout;
import java.awt.Color;

import javax.swing.AbstractListModel;
import javax.swing.BoxLayout;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import prettyprolog.datatypes.Variable;
import prettyprolog.engine.Engine;
import prettyprolog.engine.Goal;
import prettyprolog.engine.Stack;
import prettyprolog.engine.listeners.EngineEvent;
import prettyprolog.engine.listeners.EngineListener;
import prettyprolog.engine.listeners.StackEvent;
import prettyprolog.engine.listeners.StackListener;

/**
 * A Stack viewer. It shows the frames as they are pushed on the Stack, and
 * hides them when they are popped or cut.
 */
public class StackViewer extends PrettyPrologPanel {

	private static final long serialVersionUID = 1L;

	// Stack manager - manages the stack JList
	@SuppressWarnings("serial")
	private class StackManager extends AbstractListModel implements	StackListener, ListSelectionListener {

		public void framePopped(StackEvent e) {
			stack_size--;
			fireIntervalRemoved(this, stack_size, stack_size);
			showSelection();
			if (stack_size > 0)
				stack_label.setText("Frame popped");
			else
				stack_label.setText("Stack empty.");
		}

		public void frameCut(StackEvent e) {
			stack_size--;
			fireIntervalRemoved(this, stack_size, stack_size);
			showSelection();
			if (stack_size > 0)
				stack_label.setText("Cut!");
			else
				stack_label.setText("Stack empty.");
		}

		public void framePushed(StackEvent e) {
			stack_size++;
			fireIntervalAdded(this, stack_size - 1, stack_size - 1);
			showSelection();
			stack_label.setText("Frame pushed");
		}

		public void stackCleared(StackEvent e) {
			if (stack_size == 0)
				return;
			int old_stack_size = stack_size;
			stack_size = 0;
			fireIntervalRemoved(this, 0, old_stack_size);
			showFrame(null);
			stack_label.setText("Stack emptied");
		}

		public int getSize() {
			return stack_size;
		}

		public Object getElementAt(int index) {
			if (index == 0)
				return "Frame #0 (top)";
			else
				return "Frame #" + index;
		}

		public void valueChanged(ListSelectionEvent e) {
			if (e.getValueIsAdjusting())
				return;
			showSelection();
		}

	}

	private Stack stack;
	private int stack_size = 0;

	private StackManager stack_manager = new StackManager();
	private EngineListener engine_listener = new EngineListener() {

		public void solutionFound(EngineEvent e) {
			showMessage("Solution found");
		}

		public void solvingStarted() {
			showMessage("Solving...");
		}

		public void solvingContinued() {
			showMessage("Searching for alternate solutions...");
		}

		public void solvingPaused() {
			showMessage("Solving stopped.");
		}

		public void solvingAborted() {
			showMessage("Engine reset.");
		}

	};
	private JList stack_list = new JList(stack_manager);

	private JLabel goal_label = new JLabel();
	private JLabel constraint_label = new JLabel();
	private JLabel subst_label = new JLabel();
	private JLabel clause_label = new JLabel();
	private JLabel index_label = new JLabel();

	private JLabel stack_label = new JLabel(" ");

	/** Constructs a new StackViewer. */
	public StackViewer() {
		super("Stack Viewer");
		setLayout(new BorderLayout());
		stack_list.addListSelectionListener(stack_manager);
		stack_list.setPrototypeCellValue("Frame #123");
		JSplitPane split_pane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
		split_pane.setLeftComponent(new JScrollPane(stack_list));

		JPanel right_pnl = new JPanel();
		right_pnl.setLayout(new BoxLayout(right_pnl, BoxLayout.Y_AXIS));
		right_pnl.add(goal_label);
		right_pnl.add(constraint_label);
		right_pnl.add(subst_label);
		right_pnl.add(clause_label);
		right_pnl.add(index_label);
		split_pane.setRightComponent(new JScrollPane(right_pnl));
		split_pane.setResizeWeight(.3);
		add(split_pane);

		add(stack_label, BorderLayout.SOUTH);
	}

	private void showSelection() {
		int min = stack_list.getMinSelectionIndex();
		int max = stack_list.getMaxSelectionIndex();
		if (min == -1)
			showFrame(null);
		if (min == max) { // Single selection
			if (min >= 0)
				showFrame(stack.get(min));
		}
	}

	public void showHiddenVariables(boolean show) {
		super.showHiddenVariables(show);
		showSelection();
	}

	public void setOwner(PrettyPrologApplet o) {
		super.setOwner(o);
		Engine engine = o.getEngine();
		stack = engine.getStack();
		stack.addStackListener(stack_manager);
		stack_size = stack.getSize();
		engine.addEngineListener(engine_listener);
	}

	public void removeFromOwner() {
		if (owner != null) {
			owner.getEngine().removeEngineListener(engine_listener);
			stack.removeStackListener(stack_manager);
			stack_size = 0;
			stack = null;
		}
		super.removeFromOwner();
	}

	/** Shows a message in the stack label */
	private void showMessage(String msg) {
		stack_label.setText(msg);
	}

	private void showFrame(prettyprolog.engine.Frame fr) {
		if (fr == null) {
			goal_label.setText("");
			constraint_label.setText("");
			subst_label.setText("");
			clause_label.setText("");
			index_label.setText("");
			return;
		}
		goal_label.setForeground(Color.BLACK);
		constraint_label.setForeground(Color.RED);
		subst_label.setForeground(Color.BLACK);
		clause_label.setForeground(Color.BLACK);
		index_label.setForeground(Color.BLACK);
		Color goal_color = new Color(0x30, 0xb0, 0x30);
		Goal goal = fr.getGoal();
		if ((goal != null) && goal.isEmpty()) {
			goal_label.setForeground(goal_color);
			constraint_label.setForeground(Color.RED);
			subst_label.setForeground(goal_color);
			clause_label.setForeground(goal_color);
			index_label.setForeground(goal_color);
		}
		Object obj = goal;
		if (obj == null)
			obj = "(not specified)";
		goal_label.setText("Goal: " + obj.toString());
		
		obj = fr.getConstraint().getCons();
        if(obj == null)
	    obj = "(not specified)";
         constraint_label.setText("Constraint: "+obj.toString());
       
         
         
		obj = fr.getSubstitution();
		if (obj == null)
			obj = "(not specified)";
		subst_label.setText("Substitution: "
				+ ((prettyprolog.datatypes.Substitution) obj)
						.toString(show_hidden_variables));
		obj = fr.getClause();
		if (obj == null)
			obj = "(not specified)";
		clause_label.setText("Clause: " + obj.toString());
		index_label.setText("Next clause index: " + fr.getIndex());
	}
}